package org.infinitest.swingui;

import static org.easymock.EasyMock.*;
import static org.infinitest.EventFakeSupport.*;
import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.List;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;

import junit.framework.AssertionFailedError;

import org.infinitest.InfinitestCore;
import org.infinitest.ResultCollector;
import org.infinitest.TestEvent;
import org.junit.Before;
import org.junit.Test;

public class WhenDisplayingResultsInATree implements TreeModelListener
{
  private TreeModel model;
  private ResultCollector collector;
  private List<TreeModelEvent> treeEvents;

  @Before
  public void inContext()
  {
    InfinitestCore mockCore = createNiceMock(InfinitestCore.class);
    collector = new ResultCollector(mockCore);
    model = new TreeModelAdapter(collector);
    treeEvents = new ArrayList<TreeModelEvent>();
  }

  @Test
  public void shouldHaveRootNode() throws Exception
  {
    assertNotNull(model.getRoot());
  }

  @Test
  public void shouldHaveChildNodesForPointsOfFailure() throws Exception
  {
    TestEvent event = eventWithError(new AssertionFailedError());
    collector.testMethodFailed(event);
    assertEquals(1, model.getChildCount(model.getRoot()));
    assertEquals(event.getPointOfFailure(), model.getChild(model.getRoot(), 0));
  }

  @Test
  public void shouldCreateNodesForEachEvent() throws Exception
  {
    collector.testMethodFailed(eventWithError(new AssertionFailedError()));
    collector.testMethodFailed(eventWithError(new NullPointerException()));
    assertEquals(2, model.getChildCount(model.getRoot()));
  }

  @Test
  public void shouldHaveSubNodesForIndividualTests() throws Exception
  {
    TestEvent event = eventWithError(new AssertionFailedError());
    collector.testMethodFailed(event);
    Object pointOfFailureNode = model.getChild(model.getRoot(), 0);
    assertEquals(1, model.getChildCount(pointOfFailureNode));
    assertEquals(event, model.getChild(pointOfFailureNode, 0));
  }

  @Test
  public void shouldFireTreeStructureChangedWhenNewEventIsAdded() throws Exception
  {
    model.addTreeModelListener(this);
    collector.testMethodFailed(eventWithError(new AssertionFailedError()));
    assertEquals(1, treeEvents.size());
    model.removeTreeModelListener(this);
    collector.testMethodFailed(eventWithError(new AssertionFailedError()));
    assertEquals(1, treeEvents.size());
  }

  @Test
  public void shouldProvideIndexOfNodes() throws Exception
  {
    collector.testMethodFailed(eventWithError(new AssertionFailedError()));
    collector.testMethodFailed(eventWithError(new NullPointerException()));
    assertNodeReferenceIntegrity(model.getRoot(), 0);
    assertNodeReferenceIntegrity(model.getRoot(), 1);
  }

  @Test
  public void shouldIdentifyOnlyTestNodesAsLeaves() throws Exception
  {
    assertTrue(model.isLeaf(model.getRoot()));

    collector.testMethodFailed(eventWithError(new AssertionFailedError()));
    assertFalse(model.isLeaf(model.getRoot()));

    Object failureNode = model.getChild(model.getRoot(), 0);
    assertEquals(1, model.getChildCount(failureNode));
    assertFalse(model.isLeaf(failureNode));

    Object testNode = model.getChild(failureNode, 0);
    assertEquals(0, model.getChildCount(testNode));
    assertTrue(model.isLeaf(testNode));
  }

  private void assertNodeReferenceIntegrity(Object parent, int nodeIndex)
  {
    assertEquals(nodeIndex, model.getIndexOfChild(parent, model.getChild(parent, nodeIndex)));
  }

  public void treeNodesChanged(TreeModelEvent e)
  {
    throw new UnsupportedOperationException("should never be called");
  }

  public void treeNodesInserted(TreeModelEvent e)
  {
    throw new UnsupportedOperationException("should never be called");
  }

  public void treeNodesRemoved(TreeModelEvent e)
  {
    throw new UnsupportedOperationException("should never be called");
  }

  public void treeStructureChanged(TreeModelEvent e)
  {
    treeEvents.add(e);
  }
}
